package org.gdstash.db;

import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import org.gdstash.file.ARZRecord;
import org.gdstash.file.GDParseException;
import org.gdstash.util.GDConstants;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.sql.*;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class DBPetBio {
    public static final String TABLE_NAME = "GD_PET_BIO";
    public static final String FIELD_ID = "BIO_ID";
    private static final int ROW_BIO_ID = 1;
    private static final int ROW_FORMULA_LIFE = 2;
    private static final int ROW_FORMULA_MANA = 3;
    private static ConcurrentHashMap<String, DBPetBio> hashBuffer;
    private String bioID;
    private String formulaLife;
    private String formulaMana;
    private Expression expressionLife;
    private Expression expressionMana;

    private DBPetBio() {
    }

    private DBPetBio(final ARZRecord record) {
        this.bioID = record.getFileName();
        this.formulaLife = record.getBioFormulaLife();
        this.formulaMana = record.getBioFormulaMana();
    }

    static {
        DBPetBio.hashBuffer = new ConcurrentHashMap<String, DBPetBio>();
    }

    public int getLifeByPetLevel(final int level) {
        int lvl = 1;
        if (level > lvl) {
            lvl = level;
        }
        this.setPetLifeLevel(lvl);
        return (int) (this.getPetLife() + 0.1);
    }

    private void setPetLifeLevel(final int level) {
        if (this.expressionLife == null) {
            return;
        }
        this.expressionLife.setVariable("CHARLEVEL", (double) level);
    }

    private double getPetLife() {
        if (this.expressionLife == null) {
            return 1.0;
        }
        final double value = this.expressionLife.evaluate();
        return value;
    }

    public int getManaByPetLevel(final int level) {
        int lvl = 1;
        if (level > lvl) {
            lvl = level;
        }
        this.setPetManaLevel(lvl);
        return (int) (this.getPetMana() + 0.1);
    }

    private void setPetManaLevel(final int level) {
        if (this.expressionMana == null) {
            return;
        }
        this.expressionMana.setVariable("CHARLEVEL", (double) level);
    }

    private double getPetMana() {
        if (this.expressionMana == null) {
            return 1.0;
        }
        final double value = this.expressionMana.evaluate();
        return value;
    }

    private void setLifeFormula(final String formulaLife) throws GDParseException {
        this.formulaLife = formulaLife;
        if (formulaLife != null) {
            ExpressionBuilder builder = new ExpressionBuilder(formulaLife);
            builder = builder.variables(new String[]{"CHARLEVEL"});
            try {
                this.expressionLife = builder.build();
            } catch (Throwable ex) {
                this.expressionLife = null;
                throw new GDParseException(ex.getMessage());
            }
        }
    }

    public static void clearBuffer() {
        DBPetBio.hashBuffer.clear();
    }

    public static void createTables() throws SQLException {
        final String dropTable = "DROP TABLE GD_PET_BIO";
        final String createTable = "CREATE TABLE GD_PET_BIO (BIO_ID  VARCHAR(256) NOT NULL, FORMULA_LIFE      VARCHAR(256), FORMULA_MANA      VARCHAR(256), PRIMARY KEY (BIO_ID))";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, "GD_PET_BIO")) {
                    st.execute(dropTable);
                }
                st.execute(createTable);
                st.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {"GD_PET_BIO"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
                GDMsgLogger.addError(msg);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static void insert(final ARZRecord record) throws SQLException {
        final DBPetBio entry = get(record.getFileName());
        if (entry != null) {
            return;
        }
        final DBPetBio bio = new DBPetBio(record);
        final String insert = "INSERT INTO GD_PET_BIO VALUES (?,?,?)";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(insert)) {
                ps.setString(1, bio.bioID);
                ps.setString(2, bio.formulaLife);
                ps.setString(3, bio.formulaMana);
                ps.executeUpdate();
                ps.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {record.getFileName(), "GD_PET_BIO"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_INS_TABLE_BY_ID", args);
                GDMsgLogger.addLowError(msg);
                GDMsgLogger.addLowError(ex);
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static DBPetBio get(final String bioID) {
        if (bioID == null) {
            return null;
        }
        DBPetBio bio = null;
        bio = DBPetBio.hashBuffer.get(bioID);
        if (bio == null) {
            bio = getDB(bioID);
            if (bio != null) {
                DBPetBio.hashBuffer.put(bio.bioID, bio);
            }
        }
        return bio;
    }

    private static DBPetBio getDB(final String bioID) {
        DBPetBio bio = null;
        final String command = "SELECT * FROM GD_PET_BIO WHERE BIO_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, bioID);
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBPetBio> list = wrap(rs);
                if (list.isEmpty()) {
                    bio = null;
                } else {
                    bio = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {bioID, "GD_PET_BIO"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return bio;
    }

    private static List<DBPetBio> wrap(final ResultSet rs) throws SQLException {
        final LinkedList<DBPetBio> list = new LinkedList<DBPetBio>();
        while (rs.next()) {
            final DBPetBio bio = new DBPetBio();
            bio.bioID = rs.getString(1);
            bio.formulaLife = rs.getString(2);
            bio.formulaMana = rs.getString(3);
            if (bio.formulaLife != null) {
                bio.formulaLife = bio.formulaLife.toUpperCase(GDConstants.LOCALE_US);
            }
            if (bio.formulaMana != null) {
                bio.formulaMana = bio.formulaMana.toUpperCase(GDConstants.LOCALE_US);
            }
            try {
                bio.setLifeFormula(bio.formulaLife);
            } catch (GDParseException ex) {
                GDMsgLogger.addError(ex);
            }
            try {
                bio.setManaFormula(bio.formulaMana);
            } catch (GDParseException ex) {
                GDMsgLogger.addError(ex);
            }
            list.add(bio);
        }
        return list;
    }

    private void setManaFormula(final String formulaMana) throws GDParseException {
        this.formulaMana = formulaMana;
        if (formulaMana != null) {
            ExpressionBuilder builder = new ExpressionBuilder(formulaMana);
            builder = builder.variables(new String[]{"CHARLEVEL"});
            try {
                this.expressionMana = builder.build();
            } catch (Throwable ex) {
                this.expressionMana = null;
                throw new GDParseException(ex.getMessage());
            }
        }
    }
}
